﻿#include "qdatabasepool.h"
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <qdebug.h>

/**
 * 构造函数实现
 * @param settings QSettings指针，用于读取配置参数
 * @param parent 父对象
 */
QDatabasePool::QDatabasePool(QSettings *settings, QObject *parent)
    : QObject(parent)
    , settings(settings)
{
    nextConnectionId = 0;

    poolMark = settings->value("poolMark", "use").toString();
    minConnections = settings->value("minConnections", 2).toInt();
    maxConnections = settings->value("maxConnections", 10).toInt();
    databaseType = settings->value("type", "QSQLITE").toString();
    databaseHost = settings->value("host", "").toString();
    databasePort = settings->value("port", 0).toInt();
    databaseName = settings->value("name", "D://httpFrameCore.db").toString();
    databaseUser = settings->value("user", "").toString();
    databasePassword = settings->value("password", "").toString();
    init();
}

QDatabasePool::QDatabasePool(QObject *parent)
    : QObject(parent)
{
    nextConnectionId = 0;
    minConnections = 2;
    maxConnections = 10;
}

/**
 * 析构函数实现
 */
QDatabasePool::~QDatabasePool()
{
    uninit();
}

void QDatabasePool::init()
{
    createDateBase();
    initializeConnections();
}

void QDatabasePool::uninit()
{
    deinitializeConnections();
}

void QDatabasePool::createDateBase()
{
    if (databaseType == "QSQLITE")
    {
        QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, "create_temp_connection");
        db.setDatabaseName(databaseName);
        if (!db.open())
        {
            qCritical() << "Failed to create database file:" << db.lastError().text();
            return;
        }
        db.close();
        QSqlDatabase::removeDatabase("create_temp_connection");
    }
    else
    {
        QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, "initConnection"); // 使用临时连接名
        db.setHostName(databaseHost);
        db.setPort(databasePort);
        db.setUserName(databaseUser);
        db.setPassword(databasePassword);
        if (!db.open())
        {
            qCritical() << "Failed to open database connection:" << db.lastError().text();
            return;
        }
        QSqlQuery query(db); // 使用上述连接
        QString sql = QString("CREATE DATABASE IF NOT EXISTS `%1` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")
                          .arg(databaseName);
        if (!query.exec(sql))
        {
            qWarning() << "创建数据库失败：" << query.lastError().text();
            db.close();
            return;
        }
    }
}

/**
 * 获取一个数据库连接
 * @return 返回一个有效的数据库连接，如果没有可用连接则返回无效连接
 */
QSqlDatabase QDatabasePool::getConnection()
{
    QMutexLocker locker(&mutex);
    if (idleConnections.isEmpty())
    {
        // 如果空闲连接队列为空且总连接数未达到最大值，创建新连接
        if (usingConnections.size() < maxConnections)
        {
            QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, QString("%1_%2_connect_%3").arg(databaseName).arg(poolMark).arg(QString::number(nextConnectionId++)));
            db.setHostName(databaseHost);
            db.setPort(databasePort);
            db.setDatabaseName(databaseName);
            db.setUserName(databaseUser);
            db.setPassword(databasePassword);
            if (!db.open())
            {
                qCritical() << "Failed to open database connection:" << db.lastError().text();
                return QSqlDatabase();
            }
            usingConnections[nextConnectionId] = db;
            return db;
        }
        else
        {
            qWarning() << "Maximum number of connections reached";
            return QSqlDatabase(); // 返回空连接
        }
    }

    QSqlDatabase connection = idleConnections.dequeue();
    usingConnections[nextConnectionId++] = connection;
    qDebug() << "~~~~~~~~~~取走一个连接" << connection.connectionName();

    //防止异常关闭
    if (!connection.isOpen())
    {
        connection.open();
        qDebug() << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~连接异常断开，重连" << connection.connectionName();
    }
    //进行测试查询校验连接是否有效
    QSqlQuery query(connection);
    if (!query.exec("SELECT 1"))
    {
        qDebug() << "Connection check failed:" << connection.lastError();
        qDebug() << "空闲队列连接异常，进行重连";
        connection.close();
        connection.open();
    }
    return connection;
}

/**
 * 释放一个数据库连接回连接池
 * @param connection 要释放的数据库连接
 */
void QDatabasePool::releaseConnection(QSqlDatabase &connection)
{
    QMutexLocker locker(&mutex);
    qDebug() << "~~~~~~~~~~归还一个连接" << connection.connectionName();
    // 占用连接队列移出
    for (QHash<int, QSqlDatabase>::iterator it = usingConnections.begin(); it != usingConnections.end(); ++it)
    {
        if (it.value().connectionName() == connection.connectionName())
        {
            usingConnections.erase(it);
            if (connection.hostName() != databaseHost ||
                connection.port() != databasePort)
            {
                qDebug() << "连接信息更改，删除掉这个连接";
                connection.close();
            }
            else
            {
                idleConnections.enqueue(connection);
            }
            break;
        }
    }
    // 确保空闲连接数不低于最小连接数
    if (idleConnections.size() > minConnections)
    {
        while (idleConnections.size() > minConnections)
        {
            QSqlDatabase db = idleConnections.dequeue();
            db.close();
        }
    }
}

QString QDatabasePool::getDatabasePassword() const
{
    return databasePassword;
}

void QDatabasePool::setDatabasePassword(const QString &value)
{
    databasePassword = value;
}

QString QDatabasePool::getDatabaseUser() const
{
    return databaseUser;
}

void QDatabasePool::setDatabaseUser(const QString &value)
{
    databaseUser = value;
}

QString QDatabasePool::getDatabaseName() const
{
    return databaseName;
}

void QDatabasePool::setDatabaseName(const QString &value)
{
    databaseName = value;
}

int QDatabasePool::getDatabasePort() const
{
    return databasePort;
}

void QDatabasePool::setDatabasePort(int value)
{
    databasePort = value;
}

QString QDatabasePool::getDatabaseHost() const
{
    return databaseHost;
}

void QDatabasePool::setDatabaseHost(const QString &value)
{
    databaseHost = value;
}

QString QDatabasePool::getDatabaseType() const
{
    return databaseType;
}

void QDatabasePool::setDatabaseType(const QString &value)
{
    databaseType = value;
}

int QDatabasePool::getMaxConnections() const
{
    return maxConnections;
}

void QDatabasePool::setMaxConnections(int value)
{
    maxConnections = value;
}

int QDatabasePool::getMinConnections() const
{
    return minConnections;
}

void QDatabasePool::setMinConnections(int value)
{
    minConnections = value;
}

QString QDatabasePool::getPoolMark() const
{
    return poolMark;
}

void QDatabasePool::setPoolMark(const QString &value)
{
    poolMark = value;
}

void QDatabasePool::initializeConnections()
{
    QMutexLocker locker(&mutex);
    while (idleConnections.size() < minConnections)
    {
        QSqlDatabase db = QSqlDatabase::addDatabase(databaseType, QString("%1_%2_connect_%3").arg(databaseName).arg(poolMark).arg(QString::number(nextConnectionId++)));
        db.setHostName(databaseHost);
        db.setPort(databasePort);
        db.setDatabaseName(databaseName);
        db.setUserName(databaseUser);
        db.setPassword(databasePassword);

        if (!db.open())
        {
            qCritical() << "Failed to open database connection:" << db.lastError().text();
            return;
        }
        idleConnections.enqueue(db);
    }
}

void QDatabasePool::deinitializeConnections()
{
    QMutexLocker locker(&mutex);
    for (auto db : idleConnections)
    {
        db.close();
    }
    for (auto db : usingConnections.values())
    {
        db.close();
    }
    if (settings)
    {
        settings->setValue("minConnections", minConnections);
        settings->setValue("maxConnections", maxConnections);
        settings->setValue("type", databaseType);
        settings->setValue("host", databaseHost);
        settings->setValue("port", databasePort);
        settings->setValue("name", databaseName);
        settings->setValue("user", databaseUser);
        settings->setValue("password", databasePassword);
    }
}
